// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Foundation.Metadata;
using Windows.UI;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace SerialCommand
{
    public class CicuQueue<T>
    {
        uint maxLength;
        uint head;
        uint tail;
        T[] array;
        public CicuQueue(uint size)
        {
            array = new T[size];
            maxLength = size;
            head = tail = 0;
        }
        public uint MaxLength { get { return maxLength; } }
        public T this[uint index]
        {
            get
            {
                uint pos = (head + index) % maxLength;
                return array[pos];
            }
            set
            {
                uint pos = (head + index) % maxLength;
                array[pos] = value;
            }
        }
        public bool IsEmpty()
        {
            if (head == tail)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool IsFull()
        {
            if ((tail + 1) % maxLength == head)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public uint Length
        {
            get { return (tail - head + maxLength) % maxLength; }
        }
        public void Clear()
        {
            head = tail = 0;
        }
        public void In(T item)
        {
            if (IsFull())
            {
                array[tail] = item;
                tail = head++;
                if (head >= maxLength)
                    head = 0;
                return;
            }
            array[tail++] = item;
        }
        public T Out()
        {
            if (IsEmpty())
            {
                return default(T);
            }
            return array[head++];
        }
    }
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class CanvasPage : Page
    {
        CicuQueue<float> cicuQueue;
        SerialCommRegex serialCommRegex;
        public CanvasPage()
        {
            this.InitializeComponent();
            cicuQueue = new CicuQueue<float>(50000);
            serialCommRegex = SerialCommRegex.Current;
            serialCommRegex.register("wave", new ExecComm
            {
                regex = "\\[<POS:([-.0-9]+)><FORCE:([-.0-9]+)><SPEED:([-.0-9]+)><DIR:([0-9])><ERRCODE:([-.0-9]+)>..]",
                action = (string targe, Match match) =>
                {
                    string s = match.Groups[2].Value;
                    cicuQueue.In(float.Parse(s) * 100 + 285);
                    canvas.Invalidate();
                    return false;
                },
            });
        }
        double mouseX, mouseY;
        private void canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint point = e.GetCurrentPoint((CanvasControl)sender);
            mouseX = point.Position.X;
            mouseY = point.Position.Y;
        }
        private void canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint point = e.GetCurrentPoint((CanvasControl)sender);
            if (point.Properties.IsLeftButtonPressed)
            {
                double dutyX = mouseX - point.Position.X;
                double dutyY = mouseY - point.Position.Y;
                if (dutyX > 5)
                    dutyX = 5;
                if (dutyY > 5)
                    dutyY = 5;
                moveX -= Math.Abs(dutyX) * dutyX;
                moveY -= Math.Abs(dutyY) * dutyY;
                if (moveX < 0)
                    moveX = 0;
                mouseX = point.Position.X;
                mouseY = point.Position.Y;
            }

        }
        double waveScale = 1;
        double moveX = 0;
        double moveY = 0;
        private void canvas_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint point = e.GetCurrentPoint((CanvasControl)sender);
            if (point.Properties.IsHorizontalMouseWheel)
            {
                moveX -= (point.Properties.MouseWheelDelta);
                if (moveX < 0)
                    moveX = 0;
            }
            else
            {
                waveScale += (point.Properties.MouseWheelDelta / 100.0);
                if (waveScale <= 0.01)
                    waveScale = 0.01;
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            waveScale = 1;
            moveX = 0;
            moveY = 0;
        }
        float silderLen = 1;
        float silderPos = 0;
        private void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            var canvasCommandList = new CanvasCommandList(sender);
            CanvasLinearGradientBrush brush = new CanvasLinearGradientBrush(sender, Colors.Red, Colors.Yellow);
            CanvasStrokeStyle strokeStyle = new CanvasStrokeStyle()
            {
                StartCap = CanvasCapStyle.Round,
                EndCap = CanvasCapStyle.Triangle,
            };
            using (CanvasDrawingSession clds = canvasCommandList.CreateDrawingSession())
            {
                using (var builder = new CanvasPathBuilder(clds))
                {
                    uint len = cicuQueue.Length;
                    uint canvasWidth = (uint)((canvas.ActualWidth - 10) * waveScale);
                    if (canvasWidth > len)
                    {
                        canvasWidth = len;
                        silderLen = 1;
                    }
                    else
                        silderLen = canvasWidth / (float)len;
                    uint start = 0;
                    if (len > canvasWidth)
                    {
                        start = (uint)(len - canvasWidth);
                        silderPos = 1;
                        silderPos = (float)(1 - moveX / start);
                        if (moveX > start)
                        {
                            moveX = start;
                            silderPos = 0;
                        }
                        start -= (uint)moveX;
                    }
                    float yOffset = (float)moveY;
                    builder.BeginFigure(new Vector2((float)(0), cicuQueue[start] + yOffset));
                    for (uint i = 0; i < canvasWidth; i++)
                    {
                        builder.AddLine((float)(i / waveScale), cicuQueue[start + i] + yOffset);
                    }
                    builder.EndFigure(CanvasFigureLoop.Open);
                    using (var geometry = CanvasGeometry.CreatePath(builder))
                    {
                        clds.DrawGeometry(geometry, brush, 1, strokeStyle);
                    }
                }
            }
            using (var draw = args.DrawingSession)
            {
                draw.DrawImage(canvasCommandList);
            }
        }

        private void CanvasControl_Draw_1(CanvasControl sender, CanvasDrawEventArgs args)
        {
            var canvasCommandList = new CanvasCommandList(sender);
            float len = silderLen;
            using (var clds = canvasCommandList.CreateDrawingSession())
            {
                float height = (float)sender.ActualHeight;
                float width = (float)sender.ActualWidth;
                using (var path = new CanvasPathBuilder(clds))
                {
                    path.BeginFigure(new Vector2(10, height - 10));
                    path.AddLine(new Vector2(width - 10, height - 10));
                    path.AddLine(new Vector2(width - 10, 10));
                    path.EndFigure(CanvasFigureLoop.Open);
                    using (var geometry = CanvasGeometry.CreatePath(path))
                    {
                        clds.DrawGeometry(geometry, Colors.Black, 3);
                    }
                }
                Rect rect = new Rect(10, height - 5, width - 20, 5);
                if (len <= 0.015f)
                    len = 0.015f;
                float sWidth = (width - 21) * len;
                float offset = 10.5f + silderPos * ((width - 20) - sWidth);
                Rect silder = new Rect(offset, height - 4.75f, sWidth, 4.5f);
                clds.FillRoundedRectangle(silder, 2.25f, width / 2, Color.FromArgb(0x7f, 155, 155, 155));
                clds.DrawRoundedRectangle(rect, 2.5f, sWidth / 2, Color.FromArgb(0xff, 0, 0, 0));
            }
            using (var draw = args.DrawingSession)
            {
                draw.DrawImage(canvasCommandList);
            }
        }

        public void PrepareConnectedAnimation(ConnectedAnimationConfiguration config)
        {
            var anim = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("BackwardConnectedAnimation", this);

            if (config != null && ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7))
            {
                anim.Configuration = config;
            }
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("ForwardConnectedAnimation");
            if (anim != null)
            {
                anim.TryStart(this);
            }
        }

        private void Page_Unloaded(object sender, RoutedEventArgs e)
        {
            //backCanvas.RemoveFromVisualTree();
            //canvas.RemoveFromVisualTree();
            //backCanvas = null;
            //canvas = null;
        }

        ~CanvasPage()
        {
            backCanvas.RemoveFromVisualTree();
            canvas.RemoveFromVisualTree();
            backCanvas = null;
            canvas = null;
        }
    }
}
